﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Apache.Cassandra;
using Thrift.Protocol;
using Thrift.Transport;

namespace Cassandraemon
{
	public static class ClientExtensions
	{
		public static string HostName(this Cassandra.Client client)
		{
			if (client == null)
			{
				return null;
			}

			return
				HostName(client.InputProtocol) ??
				HostName(client.OutputProtocol);
		}

		public static string HostName(this TProtocol protocol)
		{
			if (protocol == null)
			{
				return null;
			}

			var socket = protocol.Transport.GetTSocket();
			if (socket != null)
			{
				return socket.Host;
			}

			return null;
		}

		internal static TSocket GetTSocket(this TTransport transport)
		{
			if(transport is TSocket) return transport as TSocket;
			
			if(transport is TFramedTransport)
			{
				var field = transport.GetType().GetField("transport", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); 
				return GetTSocket(field.GetValue(transport) as TTransport);
			}
			
			if(transport is TBufferedTransport)
			{
				var property = transport.GetType().GetProperty("UnderlyingTransport", BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance); 
				return GetTSocket(property.GetValue(transport, null) as TTransport);
			}
			
			return null;
		}

		public static void Close(this Cassandra.Client client)
		{
			if (client == null)
			{
				return;
			}

			try
			{
				CloseProtocol(client.InputProtocol);
				CloseProtocol(client.OutputProtocol);
			}
			catch (Exception err)
			{
				CassandraContext.TraceError("CloseClient([0]): {1}",
					HostName(client),
					err);

				// This may leak resources,
				// but there are not much things we can do here.
				// Ignoring is better than aborting the process.
				// We could queue the work for later execution,
				// but it might not get much anyway.
			}
		}

		static void CloseProtocol(TProtocol protocol)
		{
			var transport = protocol.Transport;
			if (transport != null)
			{
				transport.Close();
			}
		}
	}
}
